<!--
 * @Description: 深入研究长思考页面 - 节点连线展示（使用Vue Flow）
 * @Author: Auto
 * @Date: 2025-01-27
-->
<template>
  <div class="deep-thinking-container">
    <div class="header">
      <div class="header-left">
        <h2>全为数智平台</h2>
      </div>
      <div class="header-right">
        <a-button type="primary" @click="startThinking">开始思考</a-button>
        <a-button @click="autoLayout" :disabled="nodes.length <= 1">自动排版</a-button>
        <a-button @click="undo" :disabled="!canUndo">撤回</a-button>
        <a-button @click="reset">重置</a-button>
        <a-button type="primary" @click="saveNodeTree" :loading="saving">保存</a-button>
      </div>
    </div>
    <div 
      class="flow-wrapper"
      ref="flowWrapperRef"
    >
      <VueFlow
        :nodes="nodes"
        :edges="edges"
        :node-types="nodeTypes"
        :default-viewport="{ x: 0, y: 0, zoom: 1 }"
        :min-zoom="0.2"
        :max-zoom="2"
        :fit-view-on-init="false"
        :nodes-draggable="true"
        :nodes-connectable="true"
        pan-on-drag
        zoom-on-scroll
        class="vue-flow-container"
        @node-click="onNodeClick"
        @node-dblclick="onNodeDoubleClick"
        @connect="onConnect"
        @node-drag-stop="onNodeDragStop"
        @pane-click="onPaneClick"
      >
        <Background pattern-color="#e5e7eb" :gap="16" />
        <MiniMap 
          :pannable="true"
          :zoomable="false"
          mask-color="rgba(0, 0, 0, 0.1)"
          :node-stroke-width="2"
          node-color="#409EFF"
          :node-border-radius="4"
        />
      </VueFlow>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, h, nextTick } from 'vue'
import { VueFlow, useVueFlow, NodeProps } from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import { MiniMap } from '@vue-flow/minimap'
import { message } from 'ant-design-vue'
import '@vue-flow/core/dist/style.css'
import '@vue-flow/core/dist/theme-default.css'
import DeepThinkingNode from '@/components/canvasTemplate/DeepThinkingNode.vue'
import PersonInfoNode from '@/components/canvasTemplate/PersonInfoNode.vue'
import TextNode from '@/components/canvasTemplate/TextNode.vue'
import TableNode from '@/components/canvasTemplate/TableNode.vue'
import PersonTrajectoryNode from '@/components/canvasTemplate/PersonTrajectoryNode.vue'
import request from '@/utils/request'
import { 
  saveNodeTree as saveNodeTreeApi, 
  getNodeTree as getNodeTreeApi,
  getPersonInfo,
  getTableData,
  getPersonTrajectory,
  callDynamicApi
} from '@/api/fulldimensionalApi'

// 节点类型定义
type NodeType = 'root' | 'missing' | 'conclusion' | 'thinking' | 'evaluation'
type NodeStatus = 'info-missing' | 'conclusion-pending' | 'thinking' | 'evaluation'

interface CustomNodeData {
  label: string
  type: NodeType
  status?: NodeStatus
  icon?: string
  description?: string
}

// 节点配置接口定义
export interface NodeConfig {
  // 通用配置
  title?: string // 节点标题
  width?: number // 节点宽度
  height?: number // 节点高度
  progress?: number // 加载进度（0-100）
  
  // API配置
  apiUrl?: string // API接口地址
  apiMethod?: 'get' | 'post' // API请求方法
  apiParams?: Record<string, any> // API请求参数
  apiConfig?: { // API配置对象（优先级低于单独配置）
    url?: string
    method?: 'get' | 'post'
    params?: Record<string, any>
  }
  
  // 搜索字段配置（仅列表信息和人像轨迹节点）
  searchFields?: Array<{
    key: string
    label: string
    type: 'input' | 'select' | 'date' | 'daterange'
    placeholder?: string | string[] // daterange类型时可以是数组
    format?: string
    options?: Array<{ label: string; value: any }>
  }>
  
  // 样式配置
  style?: {
    borderColor?: string // 边框颜色
    backgroundColor?: string // 背景颜色
    textColor?: string // 文字颜色
  }
  
  // 节点特定配置
  // 人员信息节点
  avatar?: string
  idNumber?: string
  
  // 文本节点
  text?: string
  
  // 列表信息节点
  columns?: Array<{
    prop: string
    label: string
    width?: number
    minWidth?: number
  }>
  data?: any[]
  
  // 人像轨迹节点
  itemsPerPage?: number
}

// 使用Vue Flow
const { 
  onNodesInitialized, 
  addNodes, 
  addEdges, 
  removeNodes, 
  removeEdges,
  getViewport,
  setViewport,
  fitView,
  project,
  getNode
} = useVueFlow()

// 删除节点及其所有连线
const deleteNode = (nodeId: string) => {
  // 保存当前状态到历史记录
  saveHistory()
  
  // 删除节点
  nodes.value = nodes.value.filter(node => node.id !== nodeId)
  
  // 删除与该节点相关的所有连线（作为source或target的连线）
  edges.value = edges.value.filter(
    edge => edge.source !== nodeId && edge.target !== nodeId
  )
}

// 处理节点尺寸调整
const handleNodeResize = (nodeId: string, width: number, height: number) => {
  const node = nodes.value.find(n => n.id === nodeId)
  if (node) {
    // 更新节点的宽高数据
    if (!node.data) {
      node.data = {}
    }
    node.data.width = width
    node.data.height = height
    
    // 更新节点的style属性（Vue Flow使用style来控制节点尺寸）
    if (!node.style) {
      node.style = {}
    }
    node.style.width = `${width}px`
    node.style.height = `${height}px`
  }
}

// 处理节点数据更新（用于文本框节点）
const handleNodeUpdate = (nodeId: string, data: { title?: string; text?: string }) => {
  const node = nodes.value.find(n => n.id === nodeId)
  if (node) {
    if (!node.data) {
      node.data = {}
    }
    if (data.title !== undefined) {
      node.data.title = data.title
    }
    if (data.text !== undefined) {
      node.data.text = data.text
    }
  }
}

// 处理拖拽身份证号码创建人员节点
const handleCreatePersonNode = async (sourceNodeId: string, idNumber: string) => {
  console.log('handleCreatePersonNode 被调用:', { sourceNodeId, idNumber })
  
  const sourceNode = nodes.value.find(n => n.id === sourceNodeId)
  if (!sourceNode) {
    console.warn('源节点不存在:', sourceNodeId)
    return
  }

  // 检查是否已存在该身份证号码的节点
  const existingNode = nodes.value.find(n => 
    n.type === 'person-info' && n.data?.idNumber === idNumber
  )
  
  if (existingNode) {
    console.log('节点已存在，创建连线:', existingNode.id)
    // 如果节点已存在，创建连线
    const existingEdge = edges.value.find(
      (edge: any) => edge.source === sourceNodeId && edge.target === existingNode.id
    )
    if (!existingEdge) {
      // 保存当前状态到历史记录
      saveHistory()
      
      const edge = createEdge(
        `edge-${sourceNodeId}-${existingNode.id}-${Date.now()}`,
        sourceNodeId,
        existingNode.id,
        '关联'
      )
      edges.value.push(edge)
      console.log('连线已创建')
    } else {
      console.log('连线已存在')
    }
    return
  }

  console.log('创建新的人员节点')
  
  // 保存当前状态到历史记录
  saveHistory()
  
  // 生成新节点ID
  const nodeId = `person-info-${Date.now()}`
  
  // 计算新节点位置（基于源节点当前位置，默认在右边）
  const position = calculatePositionForManualAdd(
    sourceNode,
    nodes.value,
    200,
    150
  )
  
  console.log('新节点位置:', position)
  
  // 创建人员基本信息节点
  const newNode = createNode(
    nodeId,
    position,
    {
      avatar: '',
      idNumber: idNumber
    },
    0, // 初始进度为0，显示进度条
    true,
    200,
    150,
    'person-info' // 节点类型
  )
  
  // 添加节点
  nodes.value.push(newNode)
  console.log('节点已添加:', nodeId)
  
  // 创建连线
  const edge = createEdge(
    `edge-${sourceNodeId}-${nodeId}`,
    sourceNodeId,
    nodeId,
    '关联'
  )
  edges.value.push(edge)
  console.log('连线已创建')
  
  // 请求节点数据并更新进度
  await fetchNodeDataAndUpdateProgress(nodeId, 'person-info', { idNumber })
}

// 请求节点数据并更新进度
const fetchNodeDataAndUpdateProgress = async (
  nodeId: string, 
  nodeType: 'person-info' | 'text' | 'table' | 'person-trajectory',
  params: any = {}
) => {
  const node = nodes.value.find(n => n.id === nodeId)
  if (!node) return

  try {
    // 更新进度：0% -> 30%
    updateNodeProgress(nodeId, 30)

    let response: any = null
    
    // 从节点数据中获取API配置
    const apiUrl = node.data?.apiUrl || node.data?.apiConfig?.url
    const apiParams = node.data?.apiParams || node.data?.apiConfig?.params || {}
    const apiMethod = node.data?.apiMethod || node.data?.apiConfig?.method || 'get'
    
    // 合并参数：优先使用传入的params，然后合并apiParams
    const finalParams = { ...apiParams, ...params }
    
    // 根据节点类型请求不同的接口
    if (nodeType === 'person-info') {
      // 人员信息节点：如果配置了apiUrl，使用动态API，否则使用默认API
      if (apiUrl) {
        response = await callDynamicApi(finalParams, apiUrl, apiMethod)
      } else {
        response = await getPersonInfo({ idNumber: params.idNumber || params.idCard || '' })
      }
    } else if (nodeType === 'table') {
      // 列表信息节点：如果配置了apiUrl，使用动态API，否则使用默认API
      if (apiUrl) {
        response = await callDynamicApi(finalParams, apiUrl, apiMethod)
      } else {
        response = await getTableData(finalParams)
      }
    } else if (nodeType === 'person-trajectory') {
      // 人像轨迹节点：如果配置了apiUrl，使用动态API，否则使用默认API
      if (apiUrl) {
        response = await callDynamicApi(finalParams, apiUrl, apiMethod)
      } else {
        response = await getPersonTrajectory({ idNumber: params.idNumber || params.idCard || '' })
      }
    } else if (nodeType === 'text') {
      // 文本节点不需要请求数据，直接完成
      updateNodeProgress(nodeId, 100)
      return
    }

    // 更新进度：30% -> 80%
    updateNodeProgress(nodeId, 80)

    // 处理响应数据
    if (response && response.code === 200) {
      const result = response.result || response.data || {}
      
      // 更新节点数据
      if (nodeType === 'person-info') {
        node.data.avatar = result.avatar || ''
        node.data.idNumber = result.idNumber || result.idCard || params.idNumber || ''
      } else if (nodeType === 'table') {
        node.data.title = result.title || node.data.title || '列表信息'
        node.data.columns = result.columns || node.data.columns || []
        node.data.data = result.data || result.records || []
        // 如果配置了搜索字段，更新搜索字段配置
        if (result.searchFields) {
          node.data.searchFields = result.searchFields
        }
      } else if (nodeType === 'person-trajectory') {
        node.data.title = result.title || node.data.title || '人像轨迹'
        node.data.data = result.data || result.records || []
        node.data.itemsPerPage = result.itemsPerPage || node.data.itemsPerPage || 5
        // 如果配置了搜索字段，更新搜索字段配置
        if (result.searchFields) {
          node.data.searchFields = result.searchFields
        }
      }
      
      // 更新进度：80% -> 100%
      updateNodeProgress(nodeId, 100)
    } else {
      // 请求失败，设置为100%显示空状态
      updateNodeProgress(nodeId, 100)
    }
  } catch (error) {
    console.error('请求节点数据失败:', error)
    // 请求失败，设置为100%显示空状态
    updateNodeProgress(nodeId, 100)
  }
}


// 处理添加节点事件（支持传入配置）
const handleAddNode = async (
  parentId: string, 
  nodeType: 'person-info' | 'text' | 'table' | 'person-trajectory',
  config?: NodeConfig
) => {
  // 使用 nextTick 确保节点位置已更新（如果节点刚被拖拽）
  await nextTick()
  
  // 优先使用 getNode 获取最新位置，如果不存在则使用 nodes.value 中的节点
  let parentNode = getNode.value(parentId) || nodes.value.find(n => n.id === parentId)
  if (!parentNode) return

  // 确保使用最新的位置信息（优先使用 nodes.value 中的位置，因为拖拽后会被更新）
  const nodeInArray = nodes.value.find(n => n.id === parentId)
  const parentPosition = (nodeInArray?.position || parentNode.position) || { x: 0, y: 0 }
  
  // 创建一个包含最新位置的父节点对象
  const parentNodeWithLatestPosition = {
    ...parentNode,
    position: parentPosition
  }

  // 生成新节点ID
  const nodeId = `${nodeType}-${Date.now()}`
  
  // 根据节点类型定义尺寸
  let newNodeWidth = 250
  let newNodeHeight = 100
  
  // 根据节点类型创建节点数据
  let newNode: any
  let edgeLabel = ''

  // 获取父节点的身份证号码（如果有）
  const parentIdNumber = parentNodeWithLatestPosition.data?.idNumber || parentNodeWithLatestPosition.data?.idCard || ''
  
  // 默认配置
  const defaultConfig: NodeConfig = {}
  
  // 根据节点类型设置默认配置
  if (nodeType === 'person-info') {
    newNodeWidth = config?.width || 200
    newNodeHeight = config?.height || 150
    defaultConfig.idNumber = parentIdNumber || config?.idNumber || ''
    defaultConfig.avatar = config?.avatar || ''
    defaultConfig.title = config?.title
    edgeLabel = '人员信息'
  } else if (nodeType === 'text') {
    newNodeWidth = config?.width || 300
    newNodeHeight = config?.height || 150
    defaultConfig.title = config?.title || '文本标题'
    defaultConfig.text = config?.text || '这是一段文本内容，可以根据需要进行编辑和修改。'
    edgeLabel = '文本信息'
  } else if (nodeType === 'table') {
    newNodeWidth = config?.width || 600
    newNodeHeight = config?.height || 400
    defaultConfig.title = config?.title || '列表信息'
    defaultConfig.columns = config?.columns || [
      { prop: 'name', label: '姓名', width: 100 },
      { prop: 'age', label: '年龄', width: 80 },
      { prop: 'address', label: '证件号码', minWidth: 150 }
    ]
    defaultConfig.data = config?.data || []
    // 如果没有传入搜索字段配置，添加默认的输入框和下拉框
    if (!config?.searchFields || config.searchFields.length === 0) {
      defaultConfig.searchFields = [
        {
          key: 'name',
          label: '姓名',
          type: 'input',
          placeholder: '请输入姓名'
        },
        {
          key: 'status',
          label: '状态',
          type: 'select',
          placeholder: '请选择状态',
          options: [
            { label: '启用', value: '1' },
            { label: '禁用', value: '0' }
          ]
        }
      ]
    }
    edgeLabel = '列表信息'
  } else if (nodeType === 'person-trajectory') {
    newNodeWidth = config?.width || 850
    newNodeHeight = config?.height || 500
    defaultConfig.title = config?.title || '人像轨迹'
    defaultConfig.data = config?.data || []
    defaultConfig.itemsPerPage = config?.itemsPerPage || 5
    // 如果没有传入搜索字段配置，添加默认的时间选择框
    if (!config?.searchFields || config.searchFields.length === 0) {
      defaultConfig.searchFields = [
        {
          key: 'time',
          label: '时间范围',
          type: 'daterange',
          format: 'YYYY-MM-DD',
          placeholder: ['开始时间', '结束时间']
        }
      ]
    }
    edgeLabel = '人像轨迹'
  }
  
  // 合并配置：传入的config优先，然后使用默认配置
  const finalConfig: NodeConfig = {
    ...defaultConfig,
    ...config,
    width: newNodeWidth,
    height: newNodeHeight,
    // 如果config中没有提供某些字段，使用默认值
    title: config?.title !== undefined ? config.title : defaultConfig.title,
    columns: config?.columns || defaultConfig.columns,
    data: config?.data !== undefined ? config.data : defaultConfig.data,
    itemsPerPage: config?.itemsPerPage || defaultConfig.itemsPerPage,
    idNumber: config?.idNumber !== undefined ? config.idNumber : defaultConfig.idNumber,
    avatar: config?.avatar !== undefined ? config.avatar : defaultConfig.avatar,
    text: config?.text !== undefined ? config.text : defaultConfig.text,
    // 搜索字段：如果config中没有提供，使用默认配置
    searchFields: config?.searchFields && config.searchFields.length > 0 
      ? config.searchFields 
      : defaultConfig.searchFields
  }
  
  // 计算新节点位置（基于父节点当前位置，默认在右边）
  const position = calculatePositionForManualAdd(
    parentNodeWithLatestPosition,
    nodes.value,
    newNodeWidth,
    newNodeHeight
  )
  
  // 使用配置创建节点
  newNode = createNodeWithConfig(
    nodeId,
    position,
    nodeType,
    finalConfig,
    true
  )

  if (!newNode) return

  // 保存当前状态到历史记录
  saveHistory()

  // 添加节点
  nodes.value.push(newNode)

  // 移动视角到新节点
  centerViewOnNode(nodeId)

  // 创建连线
  const edge = createEdge(
    `edge-${parentId}-${nodeId}`,
    parentId,
    nodeId,
    edgeLabel
  )
  edges.value.push(edge)

  // 请求节点数据并更新进度
  await fetchNodeDataAndUpdateProgress(nodeId, nodeType, {
    idNumber: parentNodeWithLatestPosition.data?.idNumber || parentNodeWithLatestPosition.data?.idCard || ''
  })
}

// 注册自定义节点类型（响应式，支持选中状态）
const nodeTypes = computed(() => ({
  custom: (props: NodeProps) => {
    return h(DeepThinkingNode, {
      label: props.data.label || '',
      type: props.data.type || 'root',
      status: props.data.status,
      icon: props.data.icon,
      progress: props.data.progress !== undefined ? props.data.progress : 100,
      nodeId: props.id || '',
      width: props.data.width,
      height: props.data.height,
      selected: selectedNodeId.value === props.id,
      onDelete: deleteNode,
      onResize: handleNodeResize,
      onAddNode: handleAddNode
    })
  },
  'person-info': (props: NodeProps) => {
    return h(PersonInfoNode, {
      avatar: props.data.avatar,
      idNumber: props.data.idNumber,
      progress: props.data.progress !== undefined ? props.data.progress : 100,
      nodeId: props.id || '',
      width: props.data.width,
      height: props.data.height,
      selected: selectedNodeId.value === props.id,
      onDelete: deleteNode,
      onResize: handleNodeResize,
      onAddNode: handleAddNode,
      onCreatePersonNode: (idNumber: string) => handleCreatePersonNode(props.id || '', idNumber)
    })
  },
  text: (props: NodeProps) => {
    return h(TextNode, {
      title: props.data.title,
      text: props.data.text,
      progress: props.data.progress !== undefined ? props.data.progress : 100,
      nodeId: props.id || '',
      width: props.data.width,
      height: props.data.height,
      selected: selectedNodeId.value === props.id,
      onDelete: deleteNode,
      onResize: handleNodeResize,
      onUpdate: handleNodeUpdate,
      onCreatePersonNode: (idNumber: string) => handleCreatePersonNode(props.id || '', idNumber)
    })
  },
  table: (props: NodeProps) => {
    return h(TableNode, {
      title: props.data.title,
      columns: props.data.columns,
      data: props.data.data,
      progress: props.data.progress !== undefined ? props.data.progress : 100,
      nodeId: props.id || '',
      width: props.data.width,
      height: props.data.height,
      searchFields: props.data.searchFields,
      selected: selectedNodeId.value === props.id,
      onDelete: deleteNode,
      onResize: handleNodeResize,
      onCreatePersonNode: (idNumber: string) => handleCreatePersonNode(props.id || '', idNumber)
    })
  },
  'person-trajectory': (props: NodeProps) => {
    return h(PersonTrajectoryNode, {
      data: props.data.data || [],
      itemsPerPage: props.data.itemsPerPage || 10,
      title: props.data.title || '',
      progress: props.data.progress !== undefined ? props.data.progress : 100,
      nodeId: props.id || '',
      width: props.data.width,
      height: props.data.height,
      searchFields: props.data.searchFields,
      selected: selectedNodeId.value === props.id,
      onDelete: deleteNode,
      onResize: handleNodeResize
    })
  }
}))

// 响应式数据
const nodes = ref<any[]>([])
const edges = ref<any[]>([])
const elements = computed(() => [...nodes.value, ...edges.value])
const flowWrapperRef = ref<HTMLElement | null>(null)

// 统计数据
const tokenCount = ref(0)
const sourceCount = ref(0)
const remainingPoints = ref(1000)
const timeElapsed = ref(0)

// 保存状态
const saving = ref(false)

// 历史记录管理
const historyStack = ref<any[]>([]) // 历史状态栈
const maxHistorySize = 50 // 最大历史记录数量
const canUndo = computed(() => historyStack.value.length > 0)

// 保存当前状态到历史记录
const saveHistory = () => {
  const currentState = {
    nodes: JSON.parse(JSON.stringify(nodes.value)),
    edges: JSON.parse(JSON.stringify(edges.value)),
    tokenCount: tokenCount.value,
    sourceCount: sourceCount.value,
    remainingPoints: remainingPoints.value,
    timeElapsed: timeElapsed.value
  }
  
  historyStack.value.push(currentState)
  
  // 限制历史记录数量
  if (historyStack.value.length > maxHistorySize) {
    historyStack.value.shift()
  }
}

// 撤回操作
const undo = () => {
  if (historyStack.value.length === 0) {
    message.warning('没有可撤回的操作')
    return
  }
  
  const previousState = historyStack.value.pop()
  if (previousState) {
    // 恢复节点和连线
    nodes.value = previousState.nodes.map((node: any) => ({
      ...node,
      position: { ...node.position }
    }))
    edges.value = previousState.edges.map((edge: any) => ({
      ...edge
    }))
    
    // 恢复统计数据
    tokenCount.value = previousState.tokenCount
    sourceCount.value = previousState.sourceCount
    remainingPoints.value = previousState.remainingPoints
    timeElapsed.value = previousState.timeElapsed
    
    message.success('已撤回')
  }
}

// 节点尺寸常量
const NODE_WIDTH = 250
const NODE_HEIGHT = 100
const NODE_SPACING = 50 // 节点之间的最小间距

// 获取节点的实际尺寸
const getNodeSize = (node: any): { width: number; height: number } => {
  // 优先使用节点style中的尺寸
  if (node.style) {
    const width = node.style.width ? parseInt(node.style.width) : NODE_WIDTH
    const height = node.style.height ? parseInt(node.style.height) : NODE_HEIGHT
    return { width, height }
  }
  // 其次使用data中的尺寸
  if (node.data) {
    const width = node.data.width || NODE_WIDTH
    const height = node.data.height || NODE_HEIGHT
    return { width, height }
  }
  // 默认尺寸
  return { width: NODE_WIDTH, height: NODE_HEIGHT }
}

// 检查节点是否重叠（考虑节点的实际尺寸）
const checkNodeOverlap = (
  newPosition: { x: number; y: number },
  newSize: { width: number; height: number },
  existingNodes: any[]
): boolean => {
  for (const node of existingNodes) {
    const existingSize = getNodeSize(node)
    const existingPos = node.position
    
    // 计算两个节点的中心点距离
    const centerX1 = newPosition.x + newSize.width / 2
    const centerY1 = newPosition.y + newSize.height / 2
    const centerX2 = existingPos.x + existingSize.width / 2
    const centerY2 = existingPos.y + existingSize.height / 2
    
    // 计算两个节点边界框的最小距离
    const dx = Math.abs(centerX1 - centerX2)
    const dy = Math.abs(centerY1 - centerY2)
    
    // 计算两个节点边界框的最小间距
    const minDistanceX = (newSize.width + existingSize.width) / 2 + NODE_SPACING
    const minDistanceY = (newSize.height + existingSize.height) / 2 + NODE_SPACING
    
    // 如果两个方向的距离都小于最小间距，则认为重叠
    if (dx < minDistanceX && dy < minDistanceY) {
      return true
    }
  }
  return false
}

// 计算手动添加节点时的位置（默认在父节点右边）
const calculatePositionForManualAdd = (
  parentNode: any,
  existingNodes: any[],
  newNodeWidth?: number,
  newNodeHeight?: number
): { x: number; y: number } => {
  // 确保使用父节点的当前位置（如果节点被拖拽过，position 会被 Vue Flow 更新）
  const parentPosition = parentNode.position || { x: 0, y: 0 }
  const parentSize = getNodeSize(parentNode)
  const newSize = {
    width: newNodeWidth || NODE_WIDTH,
    height: newNodeHeight || NODE_HEIGHT
  }
  
  // 优先方向：右、下、上、左
  const directions = [
    { x: 1, y: 0 },   // 右（优先）
    { x: 0, y: 1 },   // 下
    { x: 0, y: -1 },  // 上
    { x: -1, y: 0 },  // 左
    { x: 1, y: 1 },   // 右下
    { x: 1, y: -1 },  // 右上
    { x: -1, y: 1 },  // 左下
    { x: -1, y: -1 }  // 左上
  ]
  
  // 基础距离：父节点宽度的一半 + 新节点宽度的一半 + 间距
  const baseDistance = (parentSize.width + newSize.width) / 2 + NODE_SPACING
  
  // 尝试每个方向
  for (const dir of directions) {
    // 计算初始位置（基于父节点的当前位置）
    let position = {
      x: parentPosition.x + dir.x * baseDistance,
      y: parentPosition.y + dir.y * baseDistance
    }
    
    // 如果这个位置不重叠，直接返回
    if (!checkNodeOverlap(position, newSize, existingNodes)) {
      return position
    }
    
    // 如果重叠，尝试增加距离
    for (let offset = 50; offset <= 500; offset += 50) {
      position = {
        x: parentPosition.x + dir.x * (baseDistance + offset),
        y: parentPosition.y + dir.y * (baseDistance + offset)
      }
      
      if (!checkNodeOverlap(position, newSize, existingNodes)) {
        return position
      }
    }
  }
  
  // 如果所有方向都重叠，返回默认位置（右边）
  return {
    x: parentPosition.x + baseDistance,
    y: parentPosition.y
  }
}

// 计算不重叠的位置（支持自定义节点尺寸）- 向右布局
const calculateNonOverlappingPosition = (
  parentNode: any,
  level: number,
  index: number,
  total: number,
  existingNodes: any[],
  newNodeWidth?: number,
  newNodeHeight?: number
): { x: number; y: number } => {
  // 获取父节点和当前节点的尺寸
  const parentSize = getNodeSize(parentNode)
  const newSize = {
    width: newNodeWidth || NODE_WIDTH,
    height: newNodeHeight || NODE_HEIGHT
  }
  
  // 基础水平距离：父节点宽度的一半 + 新节点宽度的一半 + 间距
  const baseHorizontalDistance = (parentSize.width + newSize.width) / 2 + NODE_SPACING
  
  // 垂直间距：用于同一层级多个节点时的垂直偏移
  const verticalSpacing = newSize.height + NODE_SPACING
  
  // 计算初始位置：向右排列
  // X坐标：父节点右边
  // Y坐标：如果有多个节点，垂直排列；否则与父节点对齐
  let position = {
    x: parentNode.position.x + baseHorizontalDistance,
    y: parentNode.position.y + (index - (total - 1) / 2) * verticalSpacing
  }
  
  // 如果重叠，尝试调整位置
  let attempts = 0
  const maxAttempts = 100
  
  while (checkNodeOverlap(position, newSize, existingNodes) && attempts < maxAttempts) {
    if (attempts < 50) {
      // 前50次尝试：增加水平距离
      const offset = (attempts + 1) * 50
      position = {
        x: parentNode.position.x + baseHorizontalDistance + offset,
        y: parentNode.position.y + (index - (total - 1) / 2) * verticalSpacing
      }
    } else {
      // 后续尝试：增加垂直偏移
      const verticalOffset = Math.floor((attempts - 50) / 10) * verticalSpacing
      const horizontalOffset = ((attempts - 50) % 10) * 50
      position = {
        x: parentNode.position.x + baseHorizontalDistance + horizontalOffset,
        y: parentNode.position.y + (index - (total - 1) / 2) * verticalSpacing + verticalOffset
      }
    }
    attempts++
  }
  
  // 如果仍然重叠，尝试向右移动更多距离
  if (checkNodeOverlap(position, newSize, existingNodes)) {
    // 找到所有现有节点的最大X坐标
    let maxX = 0
    existingNodes.forEach(node => {
      const nodeSize = getNodeSize(node)
      const nodeRight = node.position.x + nodeSize.width
      if (nodeRight > maxX) {
        maxX = nodeRight
      }
    })
    
    // 在新位置放置节点，确保不重叠
    position = {
      x: maxX + NODE_SPACING,
      y: parentNode.position.y + (index - (total - 1) / 2) * verticalSpacing
    }
    
    // 如果还是重叠，继续向右移动
    while (checkNodeOverlap(position, newSize, existingNodes) && attempts < maxAttempts * 2) {
      position.x += newSize.width + NODE_SPACING
      attempts++
    }
  }
  
  return position
}

// 移动视角到节点（使节点显示在可视区域中心）
const centerViewOnNode = (nodeId: string) => {
  const node = nodes.value.find(n => n.id === nodeId)
  if (!node) return
  
  // 等待DOM更新和节点渲染完成
  setTimeout(() => {
    try {
      const viewport = getViewport()
      const nodePosition = node.position
      
      // 获取画布容器尺寸（可视区域）
      const flowWrapper = document.querySelector('.flow-wrapper')
      if (!flowWrapper) return
      
      const containerWidth = flowWrapper.clientWidth
      const containerHeight = flowWrapper.clientHeight
      
      // 获取当前缩放级别
      const zoom = viewport.zoom || 1
      
      // Vue Flow坐标转换公式：
      // 画布上的点 (canvasX, canvasY) 在屏幕上的位置：
      // screenX = canvasX * zoom + viewport.x
      // screenY = canvasY * zoom + viewport.y
      
      // 要让节点显示在可视区域中心：
      // 可视区域中心屏幕坐标：(containerWidth/2, containerHeight/2)
      // 要让节点在这个位置：
      // containerWidth/2 = node.x * zoom + viewport.x
      // containerHeight/2 = node.y * zoom + viewport.y
      // 所以：
      const newViewportX = containerWidth / 2 - nodePosition.x * zoom
      const newViewportY = containerHeight / 2 - nodePosition.y * zoom
      
      // 设置新的视角
      setViewport({
        x: newViewportX,
        y: newViewportY,
        zoom: zoom
      }, { duration: 500 })
    } catch (error) {
      console.warn('移动视角失败:', error)
      // 备用方案：使用 fitView
      try {
        fitView({
          padding: 0.3,
          includeHiddenNodes: false,
          nodes: [node],
          duration: 500
        })
      } catch (e) {
        console.warn('备用视角移动也失败:', e)
      }
    }
  }, 300) // 延迟确保节点已渲染
}

// 创建节点（支持配置对象）
const createNodeWithConfig = (
  id: string,
  position: { x: number; y: number },
  nodeType: 'person-info' | 'text' | 'table' | 'person-trajectory',
  config: NodeConfig = {},
  draggable: boolean = true
): any => {
  const {
    title,
    width,
    height,
    progress = 0,
    apiUrl,
    apiMethod,
    apiParams,
    apiConfig,
    searchFields,
    style: styleConfig,
    ...nodeSpecificData
  } = config
  
  // 构建节点数据
  const nodeData: any = {
    ...nodeSpecificData,
    progress
  }
  
  // 添加标题（如果提供）
  if (title !== undefined) {
    nodeData.title = title
  }
  
  // 添加宽高
  if (width !== undefined) {
    nodeData.width = width
  }
  if (height !== undefined) {
    nodeData.height = height
  }
  
  // 添加API配置
  if (apiUrl) {
    nodeData.apiUrl = apiUrl
  }
  if (apiMethod) {
    nodeData.apiMethod = apiMethod
  }
  if (apiParams) {
    nodeData.apiParams = apiParams
  }
  if (apiConfig) {
    nodeData.apiConfig = apiConfig
  }
  
  // 添加搜索字段配置（仅列表信息和人像轨迹节点）
  if (searchFields && (nodeType === 'table' || nodeType === 'person-trajectory')) {
    nodeData.searchFields = searchFields
  }
  
  // 构建节点对象
  const node: any = {
    id,
    type: nodeType,
    position,
    draggable,
    data: nodeData
  }
  
  // 添加样式
  if (width !== undefined || height !== undefined || styleConfig) {
    node.style = {}
    if (width !== undefined) {
      node.style.width = `${width}px`
    }
    if (height !== undefined) {
      node.style.height = `${height}px`
    }
    // 样式配置可以通过CSS变量传递（如果需要）
    if (styleConfig) {
      if (styleConfig.borderColor) {
        node.style['--border-color'] = styleConfig.borderColor
      }
      if (styleConfig.backgroundColor) {
        node.style['--bg-color'] = styleConfig.backgroundColor
      }
      if (styleConfig.textColor) {
        node.style['--text-color'] = styleConfig.textColor
      }
    }
  }
  
  return node
}

// 创建节点（兼容旧版本）
const createNode = (
  id: string,
  position: { x: number; y: number },
  data: any,
  progress: number = 0,
  draggable: boolean = false,
  width?: number,
  height?: number,
  nodeType: string = 'custom'
): any => {
  const nodeData: any = {
    ...data,
    progress // 添加进度属性
  }
  
  // 如果指定了宽高，添加到data中
  if (width !== undefined) {
    nodeData.width = width
  }
  if (height !== undefined) {
    nodeData.height = height
  }
  
  const node: any = {
    id,
    type: nodeType, // 节点类型
    position,
    draggable, // 控制节点是否可拖拽
    data: nodeData
  }
  
  // 如果指定了宽高，添加到style中
  if (width !== undefined || height !== undefined) {
    node.style = {}
    if (width !== undefined) {
      node.style.width = `${width}px`
    }
    if (height !== undefined) {
      node.style.height = `${height}px`
    }
  }
  
  return node
}

// 更新节点进度
const updateNodeProgress = (nodeId: string, progress: number) => {
  const node = nodes.value.find(n => n.id === nodeId)
  if (node) {
    node.data.progress = progress
  }
}

// 节点进度动画
const animateNodeProgress = (nodeId: string, duration: number = 2000): Promise<void> => {
  return new Promise((resolve) => {
    const startTime = Date.now()
    const updateProgress = () => {
      const elapsed = Date.now() - startTime
      const progress = Math.min(100, (elapsed / duration) * 100)
      updateNodeProgress(nodeId, progress)
      
      if (progress < 100) {
        requestAnimationFrame(updateProgress)
      } else {
        resolve()
      }
    }
    updateProgress()
  })
}

// 根据两个节点的相对位置选择最优的Handle组合
const getOptimalHandles = (sourceNode: any, targetNode: any): { sourceHandle: string, targetHandle: string } => {
  const dx = targetNode.position.x - sourceNode.position.x
  const dy = targetNode.position.y - sourceNode.position.y
  
  // 计算角度（以源节点为中心）
  const angle = Math.atan2(dy, dx) * (180 / Math.PI)
  
  // 根据角度选择最优的Handle组合
  // 角度范围：-180 到 180
  // -45 到 45: 右边 -> 左边
  // 45 到 135: 下边 -> 上边
  // 135 到 -135: 左边 -> 右边
  // -135 到 -45: 上边 -> 下边
  
  let sourceHandle = 'right'
  let targetHandle = 'left'
  
  if (angle >= -45 && angle <= 45) {
    // 目标在源节点的右边
    sourceHandle = 'right'
    targetHandle = 'left'
  } else if (angle > 45 && angle <= 135) {
    // 目标在源节点的下边
    sourceHandle = 'bottom'
    targetHandle = 'top'
  } else if (angle > 135 || angle <= -135) {
    // 目标在源节点的左边
    sourceHandle = 'left'
    targetHandle = 'right'
  } else if (angle > -135 && angle < -45) {
    // 目标在源节点的上边
    sourceHandle = 'top'
    targetHandle = 'bottom'
  }
  
  return { sourceHandle, targetHandle }
}

// 创建连线
const createEdge = (
  id: string,
  source: string,
  target: string,
  label?: string,
  sourceHandle?: string,
  targetHandle?: string
): any => {
  // 如果没有指定Handle，自动选择最优的Handle组合
  if (!sourceHandle || !targetHandle) {
    const sourceNode = nodes.value.find(n => n.id === source)
    const targetNode = nodes.value.find(n => n.id === target)
    if (sourceNode && targetNode) {
      const optimalHandles = getOptimalHandles(sourceNode, targetNode)
      sourceHandle = optimalHandles.sourceHandle
      targetHandle = optimalHandles.targetHandle
    }
  }
  
  return {
    id,
    source,
    target,
    sourceHandle, // 源节点的Handle ID
    targetHandle, // 目标节点的Handle ID
    type: 'smoothstep',
    animated: true,
    style: {
      stroke: '#606266', // 更深的颜色，提高对比度
      strokeWidth: 3, // 增加线宽，更清晰
      strokeDasharray: '8,4' // 调整虚线样式，实线部分更长
    },
    label: label || '',
    labelStyle: {
      fill: '#666',
      fontSize: '12px',
      fontWeight: 500
    },
    labelBgStyle: {
      fill: '#ffffff',
      fillOpacity: 0.8
    }
  }
}

// 加载节点树数据
const loadNodeTreeData = (data: any) => {
  // 设置节点
  nodes.value = (data.nodes || []).map((node: any) => ({
    ...node,
    position: { ...node.position }
  }))

  // 设置连线
  edges.value = (data.edges || []).map((edge: any) => ({
    ...edge,
    animated: edge.animated !== undefined ? edge.animated : true // 确保有动画属性
  }))

  // 设置统计数据
  if (data.statistics) {
    tokenCount.value = data.statistics.tokenCount || 0
    sourceCount.value = data.statistics.sourceCount || 0
    remainingPoints.value = data.statistics.remainingPoints || 1000
    timeElapsed.value = data.statistics.timeElapsed || 0
  }

  // 设置视口（需要在 nextTick 中执行，确保 Vue Flow 已初始化）
  nextTick(() => {
    try {
      if (data.viewport) {
        setViewport(
          {
            x: data.viewport.x || 0,
            y: data.viewport.y || 0,
            zoom: data.viewport.zoom || 1
          },
          { duration: 0 }
        )
      }
    } catch (error) {
      console.warn('设置视口失败:', error)
    }
  })
}

// 创建默认的人员基本信息节点
const createDefaultPersonNode = () => {
  const nodeId = `person-info-${Date.now()}`
  const defaultNode = createNode(
    nodeId,
    { x: 400, y: 300 },
    {
      avatar: '',
      idNumber: '410211199805080018',
      progress: 100,
      width: 200,
      height: 150
    },
    100,
    true,
    200,
    150,
    'person-info'
  )
  
  nodes.value = [defaultNode]
  edges.value = []
  
  // 设置默认统计数据
  tokenCount.value = 0
  sourceCount.value = 0
  remainingPoints.value = 1000
  timeElapsed.value = 0
  
  // 设置默认视口
  nextTick(() => {
    try {
      setViewport(
        {
          x: 0,
          y: 0,
          zoom: 1
        },
        { duration: 0 }
      )
    } catch (error) {
      console.warn('设置视口失败:', error)
    }
  })
}

// 初始化节点树（从后端获取或创建默认节点）
const initRootNode = async () => {
  try {
    // 从后端获取保存的节点树数据
    const response = await getNodeTreeApi({})
    
    if (response && response.code === 200 && response.data) {
      // 如果有保存的数据，加载它
      const savedData = typeof response.data === 'string' ? JSON.parse(response.data) : response.data
      if (savedData && (savedData.nodes?.length > 0 || savedData.edges?.length > 0)) {
        loadNodeTreeData(savedData)
        return
      }
    }
    
    // 如果没有保存的数据，创建默认的人员基本信息节点
    createDefaultPersonNode()
  } catch (error) {
    console.error('获取节点树数据失败:', error)
    // 如果请求失败，创建默认的人员基本信息节点
    createDefaultPersonNode()
  }
}

// 开始思考 - 添加子节点（渐进式）
const startThinking = async () => {
  // 如果没有节点，先初始化
  if (nodes.value.length === 0) {
    await initRootNode()
  }

  // 检查是否有 root 节点，如果没有则创建一个
  let rootNode = nodes.value.find(n => n.id === 'root')
  if (!rootNode) {
    // 创建根节点
    rootNode = createNode(
      'root',
      { x: 400, y: 300 },
      {
        label: '林枫家天气情况',
        type: 'root',
        icon: '📊'
      },
      100
    )
    nodes.value.unshift(rootNode) // 添加到数组开头
  }

  // 更新统计数据
  tokenCount.value = 41009
  sourceCount.value = 100
  remainingPoints.value = 1042
  timeElapsed.value = 1.1

  // 顺序添加节点，每个节点先显示进度条
  await addMissingInfoNodesSequentially()
  await addConclusionNodesSequentially()
  await addThinkingNodeSequentially()
  await addEvaluationNodeSequentially()
}

// 顺序添加信息缺失节点（带进度条）
const addMissingInfoNodesSequentially = async () => {
  const rootId = 'root'
  const rootNode = nodes.value.find(n => n.id === rootId)
  if (!rootNode) return

  const missingNodes = [
    {
      id: 'missing-1',
      data: {
        label: '杭州2025年11月12日的气象站实时数据包括哪些关键指标?',
        type: 'missing' as NodeType,
        status: 'info-missing' as NodeStatus
      },
      edgeLabel: '包含',
      level: 1,
      index: 0
    },
    {
      id: 'missing-2',
      data: {
        label: '杭州气象局2025年11月12日官方发布的实时天气数据具体数值是多少?',
        type: 'missing' as NodeType,
        status: 'info-missing' as NodeStatus
      },
      edgeLabel: '提供-',
      parent: 'missing-1',
      level: 2,
      index: 0
    },
    {
      id: 'missing-3',
      data: {
        label: '杭州11月份典型天气模式与2025年11月的异常天气事件相比有哪些变化?',
        type: 'missing' as NodeType,
        status: 'info-missing' as NodeStatus
      },
      edgeLabel: '天气情况',
      level: 1,
      index: 1
    },
    {
      id: 'missing-4',
      data: {
        label: 'What are the weather forecast trends for Hangzhou from November 13 to November 19, 2025?',
        type: 'missing' as NodeType,
        status: 'info-missing' as NodeStatus
      },
      edgeLabel: '天气情况',
      level: 1,
      index: 2
    },
    {
      id: 'missing-5',
      data: {
        label: '杭州2025年11月的平均气温、降雨量与过去十年同期历史平均值相比有什么差异?',
        type: 'missing' as NodeType,
        status: 'info-missing' as NodeStatus
      },
      edgeLabel: '天气情况',
      parent: 'missing-4',
      level: 2,
      index: 0
    }
  ]

  // 按层级分组
  const nodesByLevel: Record<number, typeof missingNodes> = {}
  missingNodes.forEach(node => {
    if (!nodesByLevel[node.level]) {
      nodesByLevel[node.level] = []
    }
    nodesByLevel[node.level].push(node)
  })

  // 顺序添加每个节点
  for (const nodeConfig of missingNodes) {
    // 计算位置
    const parentId = nodeConfig.parent || rootId
    const parentNode = nodes.value.find(n => n.id === parentId) || rootNode
    const levelNodes = nodesByLevel[nodeConfig.level] || []
    const levelIndex = levelNodes.findIndex(n => n.id === nodeConfig.id)
    
    const position = calculateNonOverlappingPosition(
      parentNode,
      nodeConfig.level,
      levelIndex,
      levelNodes.length,
      nodes.value
    )

    // 先创建节点（进度为0，显示进度条）
    const newNode = createNode(nodeConfig.id, position, nodeConfig.data, 0, true)
    nodes.value.push(newNode)

    // 移动视角到新节点
    centerViewOnNode(nodeConfig.id)

    // 添加连线
    const edge = createEdge(
      `edge-${parentId}-${nodeConfig.id}`,
      parentId,
      nodeConfig.id,
      nodeConfig.edgeLabel
    )
    edges.value.push(edge)

    // 等待进度条动画完成
    await animateNodeProgress(nodeConfig.id, 2000)
  }
}

// 顺序添加结论节点（带进度条）
const addConclusionNodesSequentially = async () => {
  const parentNode = nodes.value.find(n => n.id === 'missing-4')
  if (!parentNode) return

  const position = calculateNonOverlappingPosition(
    parentNode,
    2,
    0,
    1,
    nodes.value
  )

  const conclusionNode = createNode(
    'conclusion-1',
    position,
    {
      label: '权威天气预报网站对2025年11月13日至19日杭州天气的具体预测数据是什么?',
      type: 'conclusion',
      status: 'conclusion-pending'
    },
    0,
    true
  )
  nodes.value.push(conclusionNode)

  // 移动视角到新节点
  centerViewOnNode('conclusion-1')

  const edge = createEdge(
    'edge-missing-4-conclusion-1',
    'missing-4',
    'conclusion-1',
    '趋势'
  )
  edges.value.push(edge)

  // 等待进度条动画完成
  await animateNodeProgress('conclusion-1', 2000)
}

// 顺序添加思考中节点（带进度条）
const addThinkingNodeSequentially = async () => {
  const parentNode = nodes.value.find(n => n.id === 'conclusion-1')
  if (!parentNode) return

  const position = calculateNonOverlappingPosition(
    parentNode,
    3,
    0,
    1,
    nodes.value
  )

  const thinkingNode = createNode(
    'thinking-1',
    position,
    {
      label: '正在思考中...',
      type: 'thinking',
      status: 'thinking'
    },
    0,
    true
  )
  nodes.value.push(thinkingNode)

  // 移动视角到新节点
  centerViewOnNode('thinking-1')

  const edge = createEdge(
    'edge-conclusion-1-thinking-1',
    'conclusion-1',
    'thinking-1'
  )
  edges.value.push(edge)

  // 等待进度条动画完成
  await animateNodeProgress('thinking-1', 2000)
}

// 顺序添加评估节点（带进度条）
const addEvaluationNodeSequentially = async () => {
  const parentNode = nodes.value.find(n => n.id === 'thinking-1')
  if (!parentNode) return

  const position = calculateNonOverlappingPosition(
    parentNode,
    4,
    0,
    1,
    nodes.value
  )

  const evaluationNode = createNode(
    'evaluation-1',
    position,
    {
      label: '杭州天气信息评估',
      type: 'evaluation',
      status: 'evaluation'
    },
    0,
    true
  )
  nodes.value.push(evaluationNode)

  // 移动视角到新节点
  centerViewOnNode('evaluation-1')

  const edge = createEdge(
    'edge-thinking-1-evaluation-1',
    'thinking-1',
    'evaluation-1'
  )
  edges.value.push(edge)

  // 等待进度条动画完成
  await animateNodeProgress('evaluation-1', 2000)
}

// 选中节点的ID
const selectedNodeId = ref<string | null>(null)

// 选中节点方法 - 接收节点数据作为参数
const selectNode = (nodeData: any) => {
  // 如果传入的是节点对象，获取其ID
  const nodeId = nodeData?.id || nodeData?.nodeId || null
  
  if (!nodeId) {
    console.warn('selectNode: 节点数据中缺少id或nodeId')
    return
  }
  
  // 检查节点是否存在
  const node = nodes.value.find(n => n.id === nodeId)
  if (!node) {
    console.warn(`selectNode: 未找到ID为 ${nodeId} 的节点`)
    return
  }
  
  // 如果点击的是已选中的节点，则取消选中
  if (selectedNodeId.value === nodeId) {
    selectedNodeId.value = null
  } else {
    // 选中新节点
    selectedNodeId.value = nodeId
  }
  
  console.log('节点选中状态:', selectedNodeId.value ? '已选中' : '已取消选中', nodeId)
}

// 取消选中节点
const deselectNode = () => {
  selectedNodeId.value = null
}

// 获取当前选中的节点数据
const getSelectedNode = () => {
  if (!selectedNodeId.value) return null
  return nodes.value.find(n => n.id === selectedNodeId.value) || null
}

// 暴露方法供外部组件调用
defineExpose({
  getSelectedNode,
  selectNode,
  deselectNode
})

// 画布点击事件 - 点击空白处取消选中
const onPaneClick = () => {
  deselectNode()
}

// 节点点击事件
const onNodeClick = (event: any) => {
  console.log('Node clicked:', event.node)
  // 点击节点时选中/取消选中
  selectNode(event.node)
}

// 节点双击事件
const onNodeDoubleClick = (event: any) => {
  console.log('Node double clicked:', event.node)
  // 可以在这里添加节点编辑或其他逻辑
  // 例如：打开节点详情、编辑节点内容等
}

// 处理拖拽悬停事件 - 允许放置
const onDragOver = (event: DragEvent) => {
  if (event.dataTransfer) {
    // 注意：在 dragover 中无法获取 dataTransfer 的数据，只能检查 types
    const types = Array.from(event.dataTransfer.types || [])
    console.log('dragover 事件触发', { types, target: event.target })
    
    // 支持的数据类型
    const supportedTypes = [
      'application/node-type',     // 节点类型（新增）
      'application/id-card',       // 身份证号码
      'application/text-content',  // 文本内容
      'text/plain',                 // 纯文本（兼容）
      'text/html'                  // HTML文本（兼容）
    ]
    
    // 检查是否是支持的类型
    const isSupported = supportedTypes.some(type => types.includes(type))
    
    if (isSupported) {
      event.preventDefault()
      event.stopPropagation()
      event.dataTransfer.dropEffect = 'copy'
      console.log('拖拽悬停在画布上，允许放置', { types, target: event.target })
    } else {
      // 允许其他类型的拖拽（可能是自定义类型）
      event.preventDefault()
      event.stopPropagation()
    }
  } else {
    // 如果没有 dataTransfer，也允许放置（可能是某些浏览器的兼容性问题）
    event.preventDefault()
    event.stopPropagation()
  }
}

// 处理拖拽放置事件 - 在画布上创建新节点
const onDrop = async (event: DragEvent) => {
  console.log('=== Drop 事件触发 ===', { 
    target: event.target, 
    currentTarget: event.currentTarget,
    types: event.dataTransfer?.types,
    clientX: event.clientX,
    clientY: event.clientY
  })
  
  if (!event.dataTransfer) {
    console.log('没有 dataTransfer，退出')
    return
  }
  
  event.preventDefault()
  event.stopPropagation()
  
  const types = Array.from(event.dataTransfer.types || [])
  console.log('拖拽类型:', types)
  
  // 使用 Vue Flow 的 project 函数将屏幕坐标转换为画布坐标
  const flowWrapper = document.querySelector('.flow-wrapper')
  if (!flowWrapper) return
  
  const rect = flowWrapper.getBoundingClientRect()
  const screenX = event.clientX - rect.left
  const screenY = event.clientY - rect.top
  
  // 使用 project 函数转换坐标
  const canvasPosition = project({ x: screenX, y: screenY })
  
  // 保存当前状态到历史记录
  saveHistory()
  
  let newNode: any = null
  let nodeId = ''
  let sourceNodeId = ''
  let nodeType: 'person-info' | 'text' | 'table' | 'person-trajectory' | null = null
  let parsedData: any = {}
  let idCard = ''
  
  // 优先检查是否有节点类型字段
  nodeType = event.dataTransfer.getData('application/node-type') as 'person-info' | 'text' | 'table' | 'person-trajectory' | null
  sourceNodeId = event.dataTransfer.getData('source-node-id') || ''
  
  // 1. 如果指定了节点类型，根据类型创建节点（支持完整配置）
  if (nodeType && ['person-info', 'text', 'table', 'person-trajectory'].includes(nodeType)) {
    console.log('根据type字段创建节点:', nodeType)
    
    const nodeData = event.dataTransfer.getData('application/node-data')
    
    // 尝试解析节点配置（JSON格式）
    let nodeConfig: NodeConfig = {}
    if (nodeData) {
      try {
        const parsed = JSON.parse(nodeData)
        // 如果解析的数据是配置对象，直接使用
        nodeConfig = parsed as NodeConfig
      } catch (e) {
        console.warn('节点配置解析失败，使用默认配置', e)
      }
    }
    
    // 如果没有传入搜索字段配置，添加默认搜索字段
    if (nodeType === 'table' && (!nodeConfig.searchFields || nodeConfig.searchFields.length === 0)) {
      nodeConfig.searchFields = [
        {
          key: 'name',
          label: '姓名',
          type: 'input',
          placeholder: '请输入姓名'
        },
        {
          key: 'status',
          label: '状态',
          type: 'select',
          placeholder: '请选择状态',
          options: [
            { label: '启用', value: '1' },
            { label: '禁用', value: '0' }
          ]
        }
      ]
    } else if (nodeType === 'person-trajectory' && (!nodeConfig.searchFields || nodeConfig.searchFields.length === 0)) {
      nodeConfig.searchFields = [
        {
          key: 'time',
          label: '时间范围',
          type: 'daterange',
          format: 'YYYY-MM-DD',
          placeholder: ['开始时间', '结束时间']
        }
      ]
    }
    
    // 根据节点类型计算默认位置偏移
    let offsetX = 0
    let offsetY = 0
    const defaultWidth = nodeConfig.width || (nodeType === 'table' ? 600 : nodeType === 'person-trajectory' ? 850 : nodeType === 'text' ? 300 : 200)
    const defaultHeight = nodeConfig.height || (nodeType === 'table' ? 400 : nodeType === 'person-trajectory' ? 500 : nodeType === 'text' ? 150 : 150)
    
    offsetX = -defaultWidth / 2
    offsetY = -defaultHeight / 2
    
    // 使用配置创建节点
    nodeId = `${nodeType}-${Date.now()}`
    newNode = createNodeWithConfig(
      nodeId,
      {
        x: canvasPosition.x + offsetX,
        y: canvasPosition.y + offsetY
      },
      nodeType,
      nodeConfig,
      true
    )
  }
  // 2. 处理身份证号码拖拽 - 创建人员信息节点（兼容模式）
  else if (types.includes('application/id-card')) {
    console.log('处理身份证号码拖拽')
    
    idCard = event.dataTransfer.getData('application/id-card')
    sourceNodeId = event.dataTransfer.getData('source-node-id') || ''
    nodeType = 'person-info'
    
    if (!idCard) {
      console.log('没有身份证号码数据')
      return
    }
    
    console.log('在画布上放置身份证号码:', { idCard, sourceNodeId })
    
    nodeId = `person-info-${Date.now()}`
    
    // 创建人员基本信息节点
    newNode = createNode(
      nodeId,
      { 
        x: canvasPosition.x - 100, // 减去节点宽度的一半（200/2）
        y: canvasPosition.y - 75   // 减去节点高度的一半（150/2）
      },
      {
        avatar: '',
        idNumber: idCard
      },
      0, // 初始进度为0，显示进度条
      true,
      200,
      150,
      'person-info'
    )
  }
  // 2. 处理文本内容拖拽 - 创建文本节点
  else if (types.includes('application/text-content')) {
    console.log('处理文本内容拖拽')
    
    const textContent = event.dataTransfer.getData('application/text-content')
    const textTitle = event.dataTransfer.getData('application/text-title') || '文本标题'
    sourceNodeId = event.dataTransfer.getData('source-node-id') || ''
    
    if (!textContent) {
      console.log('没有文本内容数据')
      return
    }
    
    console.log('在画布上放置文本内容:', { textContent, textTitle, sourceNodeId })
    
    nodeId = `text-${Date.now()}`
    
    // 创建文本节点
    newNode = createNode(
      nodeId,
      { 
        x: canvasPosition.x - 150, // 减去节点宽度的一半（300/2）
        y: canvasPosition.y - 75    // 减去节点高度的一半（150/2）
      },
      {
        title: textTitle,
        text: textContent
      },
      0, // 初始进度为0，显示进度条
      true,
      300,
      150,
      'text'
    )
  }
  // 3. 处理纯文本拖拽（兼容模式）- 尝试识别是身份证还是普通文本
  else if (types.includes('text/plain')) {
    console.log('处理纯文本拖拽（兼容模式）')
    
    const plainText = event.dataTransfer.getData('text/plain')
    sourceNodeId = event.dataTransfer.getData('source-node-id') || ''
    
    if (!plainText) {
      console.log('没有文本数据')
      return
    }
    
    // 判断是否是身份证号码（18位数字或17位数字+X）
    const idCardPattern = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/
    const isIdCard = idCardPattern.test(plainText.trim())
    
    if (isIdCard) {
      // 创建人员信息节点
      console.log('识别为身份证号码，创建人员信息节点')
      nodeId = `person-info-${Date.now()}`
      newNode = createNode(
        nodeId,
        { 
          x: canvasPosition.x - 100,
          y: canvasPosition.y - 75
        },
        {
          avatar: '',
          idNumber: plainText.trim()
        },
        0,
        true,
        200,
        150,
        'person-info'
      )
    } else {
      // 创建文本节点
      console.log('识别为普通文本，创建文本节点')
      nodeId = `text-${Date.now()}`
      newNode = createNode(
        nodeId,
        { 
          x: canvasPosition.x - 150,
          y: canvasPosition.y - 75
        },
        {
          title: '文本标题',
          text: plainText.trim()
        },
        0,
        true,
        300,
        150,
        'text'
      )
    }
  }
  // 4. 不支持的类型
  else {
    console.log('不支持的拖拽类型，忽略。类型:', types)
    return
  }
  
  if (!newNode) {
    console.log('节点创建失败')
    return
  }
  
  console.log('新节点对象:', newNode)
  
  // 添加节点
  nodes.value.push(newNode)
  console.log('节点已添加到数组，当前节点数量:', nodes.value.length)
  console.log('节点ID:', nodeId)
  
  // 如果源节点存在，创建连线
  if (sourceNodeId) {
    const edge = createEdge(
      `edge-${sourceNodeId}-${nodeId}`,
      sourceNodeId,
      nodeId,
      '关联'
    )
    edges.value.push(edge)
    console.log('连线已创建:', edge)
  }
  
  // 请求节点数据并更新进度
  if (nodeType === 'person-info') {
    await fetchNodeDataAndUpdateProgress(nodeId, 'person-info', {
      idNumber: parsedData?.idNumber || parsedData?.idCard || idCard || ''
    })
  } else if (nodeType === 'table') {
    await fetchNodeDataAndUpdateProgress(nodeId, 'table', {
      idNumber: parsedData?.idNumber || parsedData?.idCard || idCard || ''
    })
  } else if (nodeType === 'person-trajectory') {
    await fetchNodeDataAndUpdateProgress(nodeId, 'person-trajectory', {
      idNumber: parsedData?.idNumber || parsedData?.idCard || idCard || ''
    })
  } else if (nodeType === 'text') {
    // 文本节点不需要请求数据
    updateNodeProgress(nodeId, 100)
  } else {
    // 其他情况，设置为100%显示空状态
    updateNodeProgress(nodeId, 100)
  }
}

// 节点拖拽结束事件 - 确保节点位置被正确更新
const onNodeDragStop = (event: any) => {
  const node = event.node
  if (node && node.id) {
    // 更新 nodes.value 中对应节点的位置
    const nodeIndex = nodes.value.findIndex(n => n.id === node.id)
    if (nodeIndex !== -1 && node.position) {
      nodes.value[nodeIndex].position = { ...node.position }
    }
  }
}

// 连线连接事件
const onConnect = (connection: any) => {
  if (!connection.source || !connection.target) {
    return
  }
  
  // 检查是否已经存在相同的连线（包括Handle）
  const existingEdge = edges.value.find(
    (edge: any) => 
      edge.source === connection.source && 
      edge.target === connection.target &&
      edge.sourceHandle === connection.sourceHandle &&
      edge.targetHandle === connection.targetHandle
  )
  
  if (existingEdge) {
    console.log('连线已存在')
    return
  }
  
  // 保存当前状态到历史记录
  saveHistory()
  
  // 创建新的连线，保留用户选择的Handle信息
  const newEdge = createEdge(
    `edge-${connection.source}-${connection.target}-${Date.now()}`,
    connection.source,
    connection.target,
    '关联', // 手动连线时显示"关联"标签
    connection.sourceHandle, // 保留源Handle
    connection.targetHandle  // 保留目标Handle
  )
  
  edges.value.push(newEdge)
  console.log('连线已创建:', newEdge)
}

// 自动排版 - 根据连线关系进行层次化布局
const autoLayout = () => {
  if (nodes.value.length === 0) return
  
  // 构建节点关系图
  const nodeMap = new Map<string, any>()
  const childrenMap = new Map<string, string[]>() // 每个节点的子节点列表
  const parentMap = new Map<string, string>() // 每个节点的父节点
  const inDegree = new Map<string, number>() // 每个节点的入度
  
  // 初始化
  nodes.value.forEach(node => {
    nodeMap.set(node.id, node)
    childrenMap.set(node.id, [])
    inDegree.set(node.id, 0)
  })
  
  // 根据连线构建关系
  edges.value.forEach(edge => {
    const source = edge.source
    const target = edge.target
    
    // 添加子节点关系
    const children = childrenMap.get(source) || []
    children.push(target)
    childrenMap.set(source, children)
    
    // 设置父节点
    parentMap.set(target, source)
    
    // 增加入度
    inDegree.set(target, (inDegree.get(target) || 0) + 1)
  })
  
  // 找到根节点（入度为0的节点，如果有多个则选择第一个）
  const rootNodes = Array.from(inDegree.entries())
    .filter(([_, degree]) => degree === 0)
    .map(([id]) => id)
  
  if (rootNodes.length === 0) {
    // 如果没有根节点，选择第一个节点作为根
    rootNodes.push(nodes.value[0].id)
  }
  
  const rootId = rootNodes[0]
  const rootNode = nodeMap.get(rootId)
  
  // 计算每个节点的层级
  const levelMap = new Map<string, number>()
  const levelNodes: Record<number, string[]> = {} // 每层的节点列表
  
  // BFS遍历计算层级
  const queue: Array<{ id: string; level: number }> = [{ id: rootId, level: 0 }]
  const visited = new Set<string>()
  
  while (queue.length > 0) {
    const { id, level } = queue.shift()!
    
    if (visited.has(id)) continue
    visited.add(id)
    
    levelMap.set(id, level)
    
    if (!levelNodes[level]) {
      levelNodes[level] = []
    }
    levelNodes[level].push(id)
    
    // 添加子节点到队列
    const children = childrenMap.get(id) || []
    children.forEach(childId => {
      if (!visited.has(childId)) {
        queue.push({ id: childId, level: level + 1 })
      }
    })
  }
  
  // 处理没有连线的节点（孤立节点）
  nodes.value.forEach(node => {
    if (!levelMap.has(node.id)) {
      const level = Object.keys(levelNodes).length
      levelMap.set(node.id, level)
      if (!levelNodes[level]) {
        levelNodes[level] = []
      }
      levelNodes[level].push(node.id)
    }
  })
  
  // 计算布局参数 - 向右布局
  const horizontalSpacing = 350 // 水平间距（节点之间的水平距离）
  const verticalSpacing = 200 // 垂直间距（同一层级多个节点时的垂直距离）
  const startX = 400 // 起始X坐标
  const startY = 300 // 起始Y坐标
  
  // 计算每个节点的新位置
  const newPositions = new Map<string, { x: number; y: number }>()
  
  // 为每层节点分配位置 - 向右布局
  Object.keys(levelNodes).sort((a, b) => parseInt(a) - parseInt(b)).forEach(levelStr => {
    const level = parseInt(levelStr)
    const levelNodeIds = levelNodes[level]
    
    if (level === 0) {
      // 根节点：放在起始位置
      levelNodeIds.forEach((nodeId, index) => {
        const node = nodeMap.get(nodeId)
        const nodeSize = getNodeSize(node)
        const y = startY + (index - (levelNodeIds.length - 1) / 2) * (nodeSize.height + NODE_SPACING)
        newPositions.set(nodeId, { x: startX, y })
      })
    } else {
      // 子节点：向右排列，根据父节点位置计算
      levelNodeIds.forEach((nodeId, index) => {
        const node = nodeMap.get(nodeId)
        const nodeSize = getNodeSize(node)
        const parentId = parentMap.get(nodeId)
        
        let baseX = startX
        let baseY = startY
        
        if (parentId && newPositions.has(parentId)) {
          // 如果有父节点，基于父节点位置计算
          const parentPos = newPositions.get(parentId)!
          const parentNode = nodeMap.get(parentId)
          const parentSize = getNodeSize(parentNode)
          
          // X坐标：父节点右边
          baseX = parentPos.x + (parentSize.width + nodeSize.width) / 2 + NODE_SPACING
          baseY = parentPos.y
        } else {
          // 没有父节点，基于层级计算
          baseX = startX + level * horizontalSpacing
        }
        
        // 计算Y坐标：如果有多个节点，垂直排列
        let y = baseY + (index - (levelNodeIds.length - 1) / 2) * (nodeSize.height + NODE_SPACING)
        
        // 计算X坐标：确保不重叠
        let x = baseX
        
        // 检查是否与现有节点重叠
        let attempts = 0
        while (checkNodeOverlap({ x, y }, nodeSize, Array.from(newPositions.entries()).map(([id, pos]) => ({
          ...nodeMap.get(id),
          position: pos
        }))) && attempts < 50) {
          // 如果重叠，向右移动
          x += nodeSize.width + NODE_SPACING
          attempts++
        }
        
        newPositions.set(nodeId, { x, y })
      })
      
      // 对同一层级的节点进行最终调整，确保不重叠
      const levelPositions = levelNodeIds.map(id => ({
        id,
        pos: newPositions.get(id)!,
        size: getNodeSize(nodeMap.get(id))
      })).sort((a, b) => a.pos.x - b.pos.x)
      
      // 从左到右调整位置，确保不重叠
      for (let i = 1; i < levelPositions.length; i++) {
        const prev = levelPositions[i - 1]
        const curr = levelPositions[i]
        const minDistance = (prev.size.width + curr.size.width) / 2 + NODE_SPACING
        
        if (curr.pos.x - prev.pos.x < minDistance) {
          curr.pos.x = prev.pos.x + minDistance
          newPositions.set(curr.id, curr.pos)
        }
      }
    }
  })
  
  // 保存原始位置用于动画
  const originalPositions = new Map<string, { x: number; y: number }>()
  nodes.value.forEach(node => {
    originalPositions.set(node.id, { ...node.position })
  })
  
  // 平滑动画更新节点位置
  const duration = 600 // 动画时长（毫秒）
  const startTime = Date.now()
  
  const animate = () => {
    const elapsed = Date.now() - startTime
    const progress = Math.min(1, elapsed / duration)
    
    // 使用缓动函数（ease-out）
    const easeProgress = 1 - Math.pow(1 - progress, 3)
    
    // 创建新的节点数组，确保Vue能检测到变化
    const updatedNodes = nodes.value.map(node => {
      const newPos = newPositions.get(node.id)
      const oldPos = originalPositions.get(node.id)
      
      if (newPos && oldPos) {
        return {
          ...node,
          position: {
            x: oldPos.x + (newPos.x - oldPos.x) * easeProgress,
            y: oldPos.y + (newPos.y - oldPos.y) * easeProgress
          }
        }
      }
      return node
    })
    
    // 替换整个数组以触发响应式更新
    nodes.value = updatedNodes
    
    if (progress < 1) {
      requestAnimationFrame(animate)
    } else {
      // 动画完成，确保位置精确
      const finalNodes = nodes.value.map(node => {
        const newPos = newPositions.get(node.id)
        if (newPos) {
          return {
            ...node,
            position: newPos
          }
        }
        return node
      })
      nodes.value = finalNodes
      
      // 动画完成后，调整视角显示所有节点
      setTimeout(() => {
        fitView({
          padding: 0.2,
          duration: 500
        })
      }, 100)
    }
  }
  
  animate()
}

// 重置
const reset = () => {
  // 清空历史记录
  historyStack.value = []
  
  nodes.value = []
  edges.value = []
  tokenCount.value = 0
  sourceCount.value = 0
  remainingPoints.value = 1000
  timeElapsed.value = 0
  initRootNode()
}

// 保存节点树
const saveNodeTree = async () => {
  if (nodes.value.length === 0) {
    message.warning('当前没有节点可保存')
    return
  }

  saving.value = true
  try {
    // 获取当前视口信息
    const viewport = getViewport()
    
    // 构建节点树数据
    const nodeTreeData = {
      nodes: nodes.value.map(node => ({
        id: node.id,
        type: node.type,
        position: node.position,
        data: node.data,
        style: node.style,
        draggable: node.draggable
      })),
      edges: edges.value.map(edge => ({
        id: edge.id,
        source: edge.source,
        target: edge.target,
        sourceHandle: edge.sourceHandle,
        targetHandle: edge.targetHandle,
        type: edge.type,
        animated: edge.animated !== undefined ? edge.animated : true,
        label: edge.label,
        style: edge.style,
        labelStyle: edge.labelStyle,
        labelBgStyle: edge.labelBgStyle
      })),
      viewport: {
        x: viewport.x,
        y: viewport.y,
        zoom: viewport.zoom
      },
      statistics: {
        tokenCount: tokenCount.value,
        sourceCount: sourceCount.value,
        remainingPoints: remainingPoints.value,
        timeElapsed: timeElapsed.value
      }
    }

    // 调用后端接口保存
    const response = await saveNodeTreeApi(nodeTreeData)
    
    if (response && response.code === 200) {
      message.success('节点树保存成功')
    } else {
      message.error(response?.msg || '保存失败，请重试')
    }
  } catch (error: any) {
    console.error('保存节点树失败:', error)
    message.error(error?.message || '保存失败，请重试')
  } finally {
    saving.value = false
  }
}

// 初始化
onMounted(() => {
  initRootNode()
  
  // 确保 drop 事件监听器正确绑定到 Vue Flow 的画布元素
  const setupDropListeners = () => {
    // 尝试多个可能的选择器
    const selectors = [
      '.vue-flow__pane',
      '.vue-flow__viewport',
      '.vue-flow-container',
      '.flow-wrapper'
    ]
    
    let found = false
    for (const selector of selectors) {
      const element = document.querySelector(selector)
      if (element) {
        console.log(`找到画布元素: ${selector}`, element)
        element.addEventListener('drop', onDrop as EventListener, true)
        element.addEventListener('dragover', onDragOver as EventListener, true)
        found = true
      }
    }
    
    // 无论如何都在 document 上绑定，确保能捕获到事件
    console.log('在 document 上绑定 drop 事件监听器')
    document.addEventListener('drop', onDrop as EventListener, true)
    document.addEventListener('dragover', onDragOver as EventListener, true)
    
    if (!found) {
      console.warn('未找到画布元素，已绑定到 document')
    }
  }
  
  // 立即尝试绑定
  nextTick(() => {
    setupDropListeners()
  })
  
  // 延迟再次尝试，确保 Vue Flow 已完全渲染
  setTimeout(() => {
    setupDropListeners()
  }, 500)
})

onUnmounted(() => {
  // 清理所有可能的事件监听器
  const selectors = [
    '.vue-flow__pane',
    '.vue-flow__viewport',
    '.vue-flow-container',
    '.flow-wrapper'
  ]
  
  selectors.forEach(selector => {
    const element = document.querySelector(selector)
    if (element) {
      element.removeEventListener('drop', onDrop as EventListener, true)
      element.removeEventListener('dragover', onDragOver as EventListener, true)
    }
  })
  
  document.removeEventListener('drop', onDrop as EventListener, true)
  document.removeEventListener('dragover', onDragOver as EventListener, true)
})
</script>

<style scoped lang="less">
.deep-thinking-container {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: #f5f5f5;
  overflow: hidden;
}

.header {
  padding: 16px 24px;
  background: #ffffff;
  border-bottom: 1px solid #e8e8e8;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);

  .header-left {
    display: flex;
    align-items: center;
    gap: 24px;

    h2 {
      margin: 0;
      font-size: 20px;
      font-weight: 600;
      color: #262626;
    }

    .metrics {
      display: flex;
      gap: 16px;
      font-size: 12px;
      color: #8c8c8c;

      span {
        font-weight: 500;
      }
    }
  }

  .header-right {
    display: flex;
    gap: 12px;
  }
}

.flow-wrapper {
  flex: 1;
  position: relative;
  background: #ffffff;

  .vue-flow-container {
    width: 100%;
    height: 100%;
  }
}

// 自定义节点样式
:deep(.vue-flow__node) {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  
  .vue-flow__node-label {
    font-size: 14px;
    line-height: 1.5;
    word-wrap: break-word;
    word-break: break-all;
  }
  
  // 确保Handle可以正常交互
  .vue-flow__handle {
    pointer-events: auto !important;
    z-index: 1000 !important;
  }
}

// 优化连线路径样式 - 更清晰可见
:deep(.vue-flow__edge-path) {
  stroke-dasharray: 8 4 !important;
  stroke-width: 3 !important;
  stroke: #606266 !important;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.15)); // 添加阴影，增强可见性
  opacity: 0.9;
}

// 优化连线标签样式
:deep(.vue-flow__edge-label) {
  font-size: 13px !important;
  font-weight: 600 !important;
  fill: #303133 !important;
}

// 优化连线标签背景
:deep(.vue-flow__edge-labelbg) {
  fill: #ffffff !important;
  fill-opacity: 0.95 !important;
  stroke: #e4e7ed !important;
  stroke-width: 1 !important;
}

// MiniMap 可视区域边框样式
:deep(.vue-flow__minimap-mask) {
  fill: rgba(0, 0, 0, 0.1) !important;
  stroke: #409EFF !important;
  stroke-width: 2px !important;
  stroke-dasharray: none !important;
}

// MiniMap 节点样式
:deep(.vue-flow__minimap-node) {
  fill: #409EFF !important;
  stroke: #ffffff !important;
  stroke-width: 2px !important;
}

// 隐藏 Controls 控件组件
:deep(.vue-flow__controls) {
  display: none !important;
}

// 确保下拉框下拉菜单显示在所有节点之上
:deep(.ant-select-dropdown) {
  z-index: 10000 !important;
}

// 确保时间选择器下拉菜单显示在所有节点之上
:deep(.ant-picker-dropdown) {
  z-index: 10000 !important;
}

// 确保日期范围选择器下拉菜单显示在所有节点之上
:deep(.ant-picker-dropdown-range) {
  z-index: 10000 !important;
}

</style>

<style lang="less">
// 全局样式：确保下拉框下拉菜单显示在所有节点之上（因为下拉菜单渲染到 body 下）
.ant-select-dropdown {
  z-index: 10000 !important;
}

.ant-picker-dropdown {
  z-index: 10000 !important;
}

.ant-picker-dropdown-range {
  z-index: 10000 !important;
}
</style>